package org.apache.skywalking.apm.plugin.kepler;

import com.xiaomaoguai.fcp.pre.kepler.common.constants.KeplerCommonConstant;
import com.xiaomaoguai.fcp.pre.kepler.common.utils.JsonUtils;
import com.xiaomaoguai.fcp.pre.kepler.router.handler.RouterInfo;
import com.xiaomaoguai.fcp.pre.kepler.router.handler.api.HandlerContext;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.skywalking.apm.agent.core.context.ContextManager;
import org.apache.skywalking.apm.agent.core.context.tag.StringTag;
import org.apache.skywalking.apm.agent.core.context.trace.AbstractSpan;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult;
import org.apache.skywalking.apm.plugin.kepler.utils.WrapperDiffUtils;
import org.aspectj.lang.ProceedingJoinPoint;

import java.lang.reflect.Method;
import java.util.Map;

/**
 * @author WeiHui-Z
 * @version v1.0.0
 * @date 2019/5/17 20:48
 * @since JDK 1.8
 */
@Slf4j
public class KeplerAopInterceptor implements InstanceMethodsAroundInterceptor {

	@Override
	public void beforeMethod(EnhancedInstance enhancedInstance, Method method, Object[] allArguments, Class<?>[] argumentsTypes, MethodInterceptResult methodInterceptResult) throws Throwable {
		final ProceedingJoinPoint pjp = (ProceedingJoinPoint) allArguments[0];
		Object[] args = pjp.getArgs();
		Object object;
		if (args != null && args.length == 1 && (object = args[0]) instanceof HandlerContext) {
			@SuppressWarnings("rawtypes")
			HandlerContext handlerContext = (HandlerContext) object;
			final Map<String, Object> context = handlerContext.getBuf().getContext();
			final String productCode = (String) handlerContext.getBuf().get(KeplerCommonConstant.PRODUCT_CODE);
			final String routerName = handlerContext.getBuf().getCurrentRouterName();

			final RouterInfo routerInfo = handlerContext.getRouterInfo();
			final String handlerName = routerInfo.getName();
			final String mock = routerInfo.getMock();
			final String lockKey = routerInfo.getLockKey();
			final String unLockKey = routerInfo.getUnLockKey();
			final String url = routerInfo.getUrl();
			final String rpcUrl = routerInfo.getRpcUrl();

			AbstractSpan localSpan = ContextManager.createLocalSpan(handlerName);
			localSpan.tag(new StringTag("productCode"), productCode);
			localSpan.tag(new StringTag("routerName"), routerName);
			if (StringUtils.isNotBlank(url)) {
				localSpan.tag(new StringTag("url"), url);
			}
			if (StringUtils.isNotBlank(rpcUrl)) {
				localSpan.tag(new StringTag("rpcUrl"), rpcUrl);
			}
			if (StringUtils.isNotBlank(lockKey)) {
				localSpan.tag(new StringTag("lockKey"), lockKey);
			}
			if (StringUtils.isNotBlank(unLockKey)) {
				localSpan.tag(new StringTag("unLockKey"), unLockKey);
			}
			if (StringUtils.isNotBlank(mock)) {
				localSpan.tag(new StringTag("mock"), mock);
			}
			String json = JsonUtils.toFormatJsonString(context);
			localSpan.tag(new StringTag("beforeConvert"), json);
			ContextManager.getRuntimeContext().put(Constants.CONVERT_VALUE, json);
		}
	}

	@Override
	public Object afterMethod(EnhancedInstance enhancedInstance, Method method, Object[] allArguments, Class<?>[] argumentsTypes, Object o) throws Throwable {
		final ProceedingJoinPoint pjp = (ProceedingJoinPoint) allArguments[0];
		Object[] args = pjp.getArgs();
		Object object;
		if (args != null && args.length == 1 && (object = args[0]) instanceof HandlerContext) {
			@SuppressWarnings("rawtypes")
			HandlerContext handlerContext = (HandlerContext) object;
			final Map<String, Object> context = handlerContext.getBuf().getContext();

			final String afterJson = JsonUtils.toFormatJsonString(context);
			final String beforeJson = (String) ContextManager.getRuntimeContext().get(Constants.CONVERT_VALUE);
			final String json = WrapperDiffUtils.getDiff(afterJson, beforeJson);

			AbstractSpan activeSpan = ContextManager.activeSpan();
			activeSpan.tag(new StringTag("afterConvert"), afterJson);
			activeSpan.tag(new StringTag("diff"), json);
			ContextManager.stopSpan();
		}
		return o;
	}

	@Override
	public void handleMethodException(EnhancedInstance enhancedInstance, Method method, Object[] allArguments, Class<?>[] argumentsTypes, Throwable throwable) {
		ContextManager.activeSpan().errorOccurred().log(throwable);
	}

}
